home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Games / MAME / src / machine / mcr.c < prev    next >
C/C++ Source or Header  |  2000-04-23  |  18KB  |  775 lines

  1. /***************************************************************************
  2.  
  3.   machine.c
  4.  
  5.   Functions to emulate general aspects of the machine (RAM, ROM, interrupts,
  6.   I/O ports)
  7.  
  8.   Tapper machine started by Chris Kirmse
  9.  
  10. ***************************************************************************/
  11.  
  12. #include <stdio.h>
  13.  
  14. #include "driver.h"
  15. #include "machine/z80fmly.h"
  16. #include "machine/mcr.h"
  17. #include "sndhrdw/mcr.h"
  18. #include "cpu/m6800/m6800.h"
  19. #include "cpu/m6809/m6809.h"
  20. #include "cpu/z80/z80.h"
  21.  
  22.  
  23. #define VERBOSE 0
  24.  
  25. #if VERBOSE
  26. #define LOG(x)    logerror x
  27. #else
  28. #define LOG(x)
  29. #endif
  30.  
  31.  
  32.  
  33. /*************************************
  34.  *
  35.  *    Global variables
  36.  *
  37.  *************************************/
  38.  
  39. double mcr68_timing_factor;
  40.  
  41. UINT8 mcr_cocktail_flip;
  42.  
  43.  
  44.  
  45. /*************************************
  46.  *
  47.  *    Statics
  48.  *
  49.  *************************************/
  50.  
  51. static UINT8 m6840_status;
  52. static UINT8 m6840_status_read_since_int;
  53. static UINT8 m6840_msb_buffer;
  54. static UINT8 m6840_lsb_buffer;
  55. static struct counter_state
  56. {
  57.     UINT8    control;
  58.     UINT16    latch;
  59.     UINT16    count;
  60.     void *    timer;
  61.     double    period;
  62. } m6840_state[3];
  63.  
  64. /* MCR/68k interrupt states */
  65. static UINT8 m6840_irq_state;
  66. static UINT8 m6840_irq_vector;
  67. static UINT8 v493_irq_state;
  68. static UINT8 v493_irq_vector;
  69.  
  70. static void (*v493_callback)(int param);
  71.  
  72. static UINT8 zwackery_sound_data;
  73.  
  74. static const double m6840_counter_periods[3] = { 1.0 / 30.0, 1000000.0, 1.0 / (512.0 * 30.0) };
  75. static double m6840_internal_counter_period;    /* 68000 CLK / 10 */
  76.  
  77.  
  78.  
  79. /*************************************
  80.  *
  81.  *    Function prototypes
  82.  *
  83.  *************************************/
  84.  
  85. static void subtract_from_counter(int counter, int count);
  86.  
  87. static void mcr68_493_callback(int param);
  88. static void zwackery_493_callback(int param);
  89.  
  90. static WRITE_HANDLER( zwackery_pia_2_w );
  91. static WRITE_HANDLER( zwackery_pia_3_w );
  92. static WRITE_HANDLER( zwackery_ca2_w );
  93. static void zwackery_pia_irq(int state);
  94.  
  95. static void reload_count(int counter);
  96.  
  97.  
  98.  
  99. /*************************************
  100.  *
  101.  *    Graphics declarations
  102.  *
  103.  *************************************/
  104.  
  105. struct GfxLayout mcr_bg_layout =
  106. {
  107.     16,16,
  108.     RGN_FRAC(1,2),
  109.     4,
  110.     { RGN_FRAC(1,2)+0, RGN_FRAC(1,2)+1, 0, 1 },
  111.     {  0,  0,  2,  2,  4,  4,  6,  6,
  112.        8,  8, 10, 10, 12, 12, 14, 14 },
  113.     { 0*8,  0*8,  2*8,  2*8,
  114.       4*8,  4*8,  6*8,  6*8,
  115.       8*8,  8*8, 10*8, 10*8,
  116.      12*8, 12*8, 14*8, 14*8 },
  117.     16*8
  118. };
  119.  
  120.  
  121. struct GfxLayout mcr_sprite_layout =
  122. {
  123.     32,32,
  124.     RGN_FRAC(1,4),
  125.     4,
  126.     { 0, 1, 2, 3 },
  127.     { 0, 4,
  128.       RGN_FRAC(1,4)+0, RGN_FRAC(1,4)+4,
  129.       RGN_FRAC(2,4)+0, RGN_FRAC(2,4)+4,
  130.       RGN_FRAC(3,4)+0, RGN_FRAC(3,4)+4,
  131.       8, 12,
  132.       RGN_FRAC(1,4)+8, RGN_FRAC(1,4)+12,
  133.       RGN_FRAC(2,4)+8, RGN_FRAC(2,4)+12,
  134.       RGN_FRAC(3,4)+8, RGN_FRAC(3,4)+12,
  135.       16, 20,
  136.       RGN_FRAC(1,4)+16, RGN_FRAC(1,4)+20,
  137.       RGN_FRAC(2,4)+16, RGN_FRAC(2,4)+20,
  138.       RGN_FRAC(3,4)+16, RGN_FRAC(3,4)+20,
  139.       24, 28,
  140.       RGN_FRAC(1,4)+24, RGN_FRAC(1,4)+28,
  141.       RGN_FRAC(2,4)+24, RGN_FRAC(2,4)+28,
  142.       RGN_FRAC(3,4)+24, RGN_FRAC(3,4)+28 },
  143.     { 32*0,  32*1,  32*2,  32*3,
  144.       32*4,  32*5,  32*6,  32*7,
  145.       32*8,  32*9,  32*10, 32*11,
  146.       32*12, 32*13, 32*14, 32*15,
  147.       32*16, 32*17, 32*18, 32*19,
  148.       32*20, 32*21, 32*22, 32*23,
  149.       32*24, 32*25, 32*26, 32*27,
  150.       32*28, 32*29, 32*30, 32*31 },
  151.     32*32
  152. };
  153.  
  154.  
  155.  
  156. /*************************************
  157.  *
  158.  *    6821 PIA declarations
  159.  *
  160.  *************************************/
  161.  
  162. extern READ_HANDLER( zwackery_port_2_r );
  163.  
  164. static struct pia6821_interface zwackery_pia_2_intf =
  165. {
  166.     /*inputs : A/B,CA/B1,CA/B2 */ 0, input_port_0_r, 0, 0, 0, 0,
  167.     /*outputs: A/B,CA/B2       */ zwackery_pia_2_w, 0, 0, 0,
  168.     /*irqs   : A/B             */ zwackery_pia_irq, zwackery_pia_irq
  169. };
  170.  
  171. static struct pia6821_interface zwackery_pia_3_intf =
  172. {
  173.     /*inputs : A/B,CA/B1,CA/B2 */ input_port_1_r, zwackery_port_2_r, 0, 0, 0, 0,
  174.     /*outputs: A/B,CA/B2       */ zwackery_pia_3_w, 0, zwackery_ca2_w, 0,
  175.     /*irqs   : A/B             */ 0, 0
  176. };
  177.  
  178. static struct pia6821_interface zwackery_pia_4_intf =
  179. {
  180.     /*inputs : A/B,CA/B1,CA/B2 */ input_port_3_r, input_port_4_r, 0, 0, 0, 0,
  181.     /*outputs: A/B,CA/B2       */ 0, 0, 0, 0,
  182.     /*irqs   : A/B             */ 0, 0
  183. };
  184.  
  185.  
  186.  
  187. /*************************************
  188.  *
  189.  *    Generic MCR CTC interface
  190.  *
  191.  *************************************/
  192.  
  193. static void ctc_interrupt(int state)
  194. {
  195.     cpu_cause_interrupt(0, Z80_VECTOR(0, state));
  196. }
  197.  
  198.  
  199. Z80_DaisyChain mcr_daisy_chain[] =
  200. {
  201.     { z80ctc_reset, z80ctc_interrupt, z80ctc_reti, 0 }, /* CTC number 0 */
  202.     { 0, 0, 0, -1}         /* end mark */
  203. };
  204.  
  205.  
  206. static z80ctc_interface ctc_intf =
  207. {
  208.     1,                  /* 1 chip */
  209.     { 0 },              /* clock (filled in from the CPU 0 clock) */
  210.     { 0 },              /* timer disables */
  211.     { ctc_interrupt },  /* interrupt handler */
  212.     { 0 },              /* ZC/TO0 callback */
  213.     { 0 },              /* ZC/TO1 callback */
  214.     { 0 }               /* ZC/TO2 callback */
  215. };
  216.  
  217.  
  218.  
  219. /*************************************
  220.  *
  221.  *    Generic MCR machine initialization
  222.  *
  223.  *************************************/
  224.  
  225. void mcr_init_machine(void)
  226. {
  227.     /* initialize the CTC */
  228.     ctc_intf.baseclock[0] = Machine->drv->cpu[0].cpu_clock;
  229.     z80ctc_init(&ctc_intf);
  230.  
  231.     /* reset cocktail flip */
  232.     mcr_cocktail_flip = 0;
  233.  
  234.     /* initialize the sound */
  235.     mcr_sound_init();
  236. }
  237.  
  238.  
  239.  
  240. /*************************************
  241.  *
  242.  *    Generic MCR/68k machine initialization
  243.  *
  244.  *************************************/
  245.  
  246. static void mcr68_common_init(void)
  247. {
  248.     int i;
  249.  
  250.     /* reset the 6840's */
  251.     m6840_status = 0x00;
  252.     m6840_status_read_since_int = 0x00;
  253.     m6840_msb_buffer = m6840_lsb_buffer = 0;
  254.     for (i = 0; i < 3; i++)
  255.     {
  256.         m6840_state[i].control = 0x00;
  257.         m6840_state[i].latch = 0xffff;
  258.         m6840_state[i].count = 0xffff;
  259.         m6840_state[i].timer = NULL;
  260.         m6840_state[i].period = m6840_counter_periods[i];
  261.     }
  262.  
  263.     /* initialize the clock */
  264.     m6840_internal_counter_period = TIME_IN_HZ(Machine->drv->cpu[0].cpu_clock / 10);
  265.  
  266.     /* reset cocktail flip */
  267.     mcr_cocktail_flip = 0;
  268.  
  269.     /* initialize the sound */
  270.     pia_unconfig();
  271.     mcr_sound_init();
  272. }
  273.  
  274.  
  275. void mcr68_init_machine(void)
  276. {
  277.     /* for the most part all MCR/68k games are the same */
  278.     mcr68_common_init();
  279.     v493_callback = mcr68_493_callback;
  280.  
  281.     /* vectors are 1 and 2 */
  282.     v493_irq_vector = 1;
  283.     m6840_irq_vector = 2;
  284. }
  285.  
  286.  
  287. void zwackery_init_machine(void)
  288. {
  289.     /* for the most part all MCR/68k games are the same */
  290.     mcr68_common_init();
  291.     v493_callback = zwackery_493_callback;
  292.  
  293.     /* append our PIA state onto the existing one and reinit */
  294.     pia_config(2, PIA_STANDARD_ORDERING | PIA_16BIT_UPPER, &zwackery_pia_2_intf);
  295.     pia_config(3, PIA_STANDARD_ORDERING | PIA_16BIT_LOWER, &zwackery_pia_3_intf);
  296.     pia_config(4, PIA_STANDARD_ORDERING | PIA_16BIT_LOWER, &zwackery_pia_4_intf);
  297.     pia_reset();
  298.  
  299.     /* vectors are 5 and 6 */
  300.     v493_irq_vector = 5;
  301.     m6840_irq_vector = 6;
  302. }
  303.  
  304.  
  305.  
  306. /*************************************
  307.  *
  308.  *    Generic MCR interrupt handler
  309.  *
  310.  *************************************/
  311.  
  312. int mcr_interrupt(void)
  313. {
  314.     /* once per frame, pulse the CTC line 3 */
  315.     z80ctc_0_trg3_w(0, 1);
  316.     z80ctc_0_trg3_w(0, 0);
  317.  
  318.     return ignore_interrupt();
  319. }
  320.  
  321.  
  322. int mcr68_interrupt(void)
  323. {
  324.     /* update the 6840 VBLANK clock */
  325.     if (!m6840_state[0].timer)
  326.         subtract_from_counter(0, 1);
  327.  
  328.     logerror("--- VBLANK ---\n");
  329.  
  330.     /* also set a timer to generate the 493 signal at a specific time before the next VBLANK */
  331.     /* the timing of this is crucial for Blasted and Tri-Sports, which check the timing of */
  332.     /* VBLANK and 493 using counter 2 */
  333.     timer_set(TIME_IN_HZ(30) - mcr68_timing_factor, 0, v493_callback);
  334.  
  335.     return ignore_interrupt();
  336. }
  337.  
  338.  
  339.  
  340. /*************************************
  341.  *
  342.  *    MCR/68k interrupt central
  343.  *
  344.  *************************************/
  345.  
  346. static void update_mcr68_interrupts(void)
  347. {
  348.     int newstate = 0;
  349.  
  350.     /* all interrupts go through an LS148, which gives priority to the highest */
  351.     if (v493_irq_state)
  352.         newstate = v493_irq_vector;
  353.     if (m6840_irq_state)
  354.         newstate = m6840_irq_vector;
  355.  
  356.     /* set the new state of the IRQ lines */
  357.     if (newstate)
  358.         cpu_set_irq_line(0, newstate, ASSERT_LINE);
  359.     else
  360.         cpu_set_irq_line(0, 7, CLEAR_LINE);
  361. }
  362.  
  363.  
  364. static void mcr68_493_off_callback(int param)
  365. {
  366.     v493_irq_state = 0;
  367.     update_mcr68_interrupts();
  368. }
  369.  
  370.  
  371. static void mcr68_493_callback(int param)
  372. {
  373.     v493_irq_state = 1;
  374.     update_mcr68_interrupts();
  375.     timer_set(cpu_getscanlineperiod(), 0, mcr68_493_off_callback);
  376.     logerror("--- (INT1) ---\n");
  377. }
  378.  
  379.  
  380.  
  381. /*************************************
  382.  *
  383.  *    Generic MCR port write handlers
  384.  *
  385.  *************************************/
  386.  
  387. WRITE_HANDLER( mcr_control_port_w )
  388. {
  389.     /*
  390.         Bit layout is as follows:
  391.             D7 = n/c
  392.             D6 = cocktail flip
  393.             D5 = red LED
  394.             D4 = green LED
  395.             D3 = n/c
  396.             D2 = coin meter 3
  397.             D1 = coin meter 2
  398.             D0 = coin meter 1
  399.     */
  400.  
  401.     mcr_cocktail_flip = (data >> 6) & 1;
  402. }
  403.  
  404.  
  405. WRITE_HANDLER( mcr_scroll_value_w )
  406. {
  407.     switch (offset)
  408.     {
  409.         case 0:
  410.             /* low 8 bits of horizontal scroll */
  411.             spyhunt_scrollx = (spyhunt_scrollx & ~0xff) | data;
  412.             break;
  413.  
  414.         case 1:
  415.             /* upper 3 bits of horizontal scroll and upper 1 bit of vertical scroll */
  416.             spyhunt_scrollx = (spyhunt_scrollx & 0xff) | ((data & 0x07) << 8);
  417.             spyhunt_scrolly = (spyhunt_scrolly & 0xff) | ((data & 0x80) << 1);
  418.             break;
  419.  
  420.         case 2:
  421.             /* low 8 bits of vertical scroll */
  422.             spyhunt_scrolly = (spyhunt_scrolly & ~0xff) | data;
  423.             break;
  424.     }
  425. }
  426.  
  427.  
  428.  
  429. /*************************************
  430.  *
  431.  *    Zwackery-specific interfaces
  432.  *
  433.  *************************************/
  434.  
  435. WRITE_HANDLER( zwackery_pia_2_w )
  436. {
  437.     /* bit 7 is the watchdog */
  438.     if (!(data & 0x80)) watchdog_reset_w(offset, data);
  439.  
  440.     /* bits 5 and 6 control hflip/vflip */
  441.     /* bits 3 and 4 control coin counters? */
  442.     /* bits 0, 1 and 2 control meters? */
  443. }
  444.  
  445.  
  446. WRITE_HANDLER( zwackery_pia_3_w )
  447. {
  448.     zwackery_sound_data = (data >> 4) & 0x0f;
  449. }
  450.  
  451.  
  452. WRITE_HANDLER( zwackery_ca2_w )
  453. {
  454.     csdeluxe_data_w(offset, (data << 4) | zwackery_sound_data);
  455. }
  456.  
  457.  
  458. void zwackery_pia_irq(int state)
  459. {
  460.     v493_irq_state = state;
  461.     update_mcr68_interrupts();
  462. }
  463.  
  464.  
  465. static void zwackery_493_off_callback(int param)
  466. {
  467.     pia_2_ca1_w(0, 0);
  468. }
  469.  
  470.  
  471. static void zwackery_493_callback(int param)
  472. {
  473.     pia_2_ca1_w(0, 1);
  474.     timer_set(cpu_getscanlineperiod(), 0, zwackery_493_off_callback);
  475. }
  476.  
  477.  
  478.  
  479. /*************************************
  480.  *
  481.  *    M6840 timer utilities
  482.  *
  483.  *************************************/
  484.  
  485. INLINE void update_interrupts(void)
  486. {
  487.     m6840_status &= ~0x80;
  488.  
  489.     if ((m6840_status & 0x01) && (m6840_state[0].control & 0x40)) m6840_status |= 0x80;
  490.     if ((m6840_status & 0x02) && (m6840_state[1].control & 0x40)) m6840_status |= 0x80;
  491.     if ((m6840_status & 0x04) && (m6840_state[2].control & 0x40)) m6840_status |= 0x80;
  492.  
  493.     m6840_irq_state = m6840_status >> 7;
  494.     update_mcr68_interrupts();
  495. }
  496.  
  497.  
  498. static void subtract_from_counter(int counter, int count)
  499. {
  500.     /* dual-byte mode */
  501.     if (m6840_state[counter].control & 0x04)
  502.     {
  503.         int lsb = m6840_state[counter].count & 0xff;
  504.         int msb = m6840_state[counter].count >> 8;
  505.  
  506.         /* count the clocks */
  507.         lsb -= count;
  508.  
  509.         /* loop while we're less than zero */
  510.         while (lsb < 0)
  511.         {
  512.             /* borrow from the MSB */
  513.             lsb += (m6840_state[counter].latch & 0xff) + 1;
  514.             msb--;
  515.  
  516.             /* if MSB goes less than zero, we've expired */
  517.             if (msb < 0)
  518.             {
  519.                 m6840_status |= 1 << counter;
  520.                 m6840_status_read_since_int &= ~(1 << counter);
  521.                 update_interrupts();
  522.                 msb = (m6840_state[counter].latch >> 8) + 1;
  523.                 LOG(("** Counter %d fired\n", counter));
  524.             }
  525.         }
  526.  
  527.         /* store the result */
  528.         m6840_state[counter].count = (msb << 8) | lsb;
  529.     }
  530.  
  531.     /* word mode */
  532.     else
  533.     {
  534.         int word = m6840_state[counter].count;
  535.  
  536.         /* count the clocks */
  537.         word -= count;
  538.  
  539.         /* loop while we're less than zero */
  540.         while (word < 0)
  541.         {
  542.             /* borrow from the MSB */
  543.             word += m6840_state[counter].latch + 1;
  544.  
  545.             /* we've expired */
  546.             m6840_status |= 1 << counter;
  547.             m6840_status_read_since_int &= ~(1 << counter);
  548.             update_interrupts();
  549.             LOG(("** Counter %d fired\n", counter));
  550.         }
  551.  
  552.         /* store the result */
  553.         m6840_state[counter].count = word;
  554.     }
  555. }
  556.  
  557.  
  558. static void counter_fired_callback(int counter)
  559. {
  560.     int count = counter >> 2;
  561.     counter &= 3;
  562.  
  563.     /* reset the timer */
  564.     m6840_state[counter].timer = NULL;
  565.  
  566.     /* subtract it all from the counter; this will generate an interrupt */
  567.     subtract_from_counter(counter, count);
  568. }
  569.  
  570.  
  571. static void reload_count(int counter)
  572. {
  573.     double period;
  574.     int count;
  575.  
  576.     /* copy the latched value in */
  577.     m6840_state[counter].count = m6840_state[counter].latch;
  578.  
  579.     /* remove any old timers */
  580.     if (m6840_state[counter].timer)
  581.         timer_remove(m6840_state[counter].timer);
  582.     m6840_state[counter].timer = NULL;
  583.  
  584.     /* counter 0 is self-updating if clocked externally */
  585.     if (counter == 0 && !(m6840_state[counter].control & 0x02))
  586.         return;
  587.  
  588.     /* determine the clock period for this timer */
  589.     if (m6840_state[counter].control & 0x02)
  590.         period = m6840_internal_counter_period;
  591.     else
  592.         period = m6840_counter_periods[counter];
  593.  
  594.     /* determine the number of clock periods before we expire */
  595.     count = m6840_state[counter].count;
  596.     if (m6840_state[counter].control & 0x04)
  597.         count = ((count >> 8) + 1) * ((count & 0xff) + 1);
  598.     else
  599.         count = count + 1;
  600.  
  601.     /* set the timer */
  602.     m6840_state[counter].timer = timer_set(period * (double)count, (count << 2) + counter, counter_fired_callback);
  603. }
  604.  
  605.  
  606. static UINT16 compute_counter(int counter)
  607. {
  608.     double period;
  609.     int remaining;
  610.  
  611.     /* if there's no timer, return the count */
  612.     if (!m6840_state[counter].timer)
  613.         return m6840_state[counter].count;
  614.  
  615.     /* determine the clock period for this timer */
  616.     if (m6840_state[counter].control & 0x02)
  617.         period = m6840_internal_counter_period;
  618.     else
  619.         period = m6840_counter_periods[counter];
  620.  
  621.     /* see how many are left */
  622.     remaining = (int)(timer_timeleft(m6840_state[counter].timer) / period);
  623.  
  624.     /* adjust the count for dual byte mode */
  625.     if (m6840_state[counter].control & 0x04)
  626.     {
  627.         int divisor = (m6840_state[counter].count & 0xff) + 1;
  628.         int msb = remaining / divisor;
  629.         int lsb = remaining % divisor;
  630.         remaining = (msb << 8) | lsb;
  631.     }
  632.  
  633.     return remaining;
  634. }
  635.  
  636.  
  637.  
  638. /*************************************
  639.  *
  640.  *    M6840 timer I/O
  641.  *
  642.  *************************************/
  643.  
  644. static WRITE_HANDLER( mcr68_6840_w_common )
  645. {
  646.     int i;
  647.  
  648.     /* offsets 0 and 1 are control registers */
  649.     if (offset < 2)
  650.     {
  651.         int counter = (offset == 1) ? 1 : (m6840_state[1].control & 0x01) ? 0 : 2;
  652.         UINT8 diffs = data ^ m6840_state[counter].control;
  653.  
  654.         m6840_state[counter].control = data;
  655.  
  656.         /* reset? */
  657.         if (counter == 0 && (diffs & 0x01))
  658.         {
  659.             /* holding reset down */
  660.             if (data & 0x01)
  661.             {
  662.                 for (i = 0; i < 3; i++)
  663.                 {
  664.                     if (m6840_state[i].timer)
  665.                         timer_remove(m6840_state[i].timer);
  666.                     m6840_state[i].timer = NULL;
  667.                 }
  668.             }
  669.  
  670.             /* releasing reset */
  671.             else
  672.             {
  673.                 for (i = 0; i < 3; i++)
  674.                     reload_count(i);
  675.             }
  676.  
  677.             m6840_status = 0;
  678.             update_interrupts();
  679.         }
  680.  
  681.         /* changing the clock source? (needed for Zwackery) */
  682.         if (diffs & 0x02)
  683.             reload_count(counter);
  684.  
  685.         LOG(("%06X:Counter %d control = %02X\n", cpu_getpreviouspc(), counter, data));
  686.     }
  687.  
  688.     /* offsets 2, 4, and 6 are MSB buffer registers */
  689.     else if ((offset & 1) == 0)
  690.     {
  691.         LOG(("%06X:MSB = %02X\n", cpu_getpreviouspc(), data));
  692.         m6840_msb_buffer = data;
  693.     }
  694.  
  695.     /* offsets 3, 5, and 7 are Write Timer Latch commands */
  696.     else
  697.     {
  698.         int counter = (offset - 2) / 2;
  699.         m6840_state[counter].latch = (m6840_msb_buffer << 8) | (data & 0xff);
  700.  
  701.         /* clear the interrupt */
  702.         m6840_status &= ~(1 << counter);
  703.         update_interrupts();
  704.  
  705.         /* reload the count if in an appropriate mode */
  706.         if (!(m6840_state[counter].control & 0x10))
  707.             reload_count(counter);
  708.  
  709.         LOG(("%06X:Counter %d latch = %04X\n", cpu_getpreviouspc(), counter, m6840_state[counter].latch));
  710.     }
  711. }
  712.  
  713.  
  714. static READ_HANDLER( mcr68_6840_r_common )
  715. {
  716.     /* offset 0 is a no-op */
  717.     if (offset == 0)
  718.         return 0;
  719.  
  720.     /* offset 1 is the status register */
  721.     else if (offset == 1)
  722.     {
  723.         LOG(("%06X:Status read = %04X\n", cpu_getpreviouspc(), m6840_status));
  724.         m6840_status_read_since_int |= m6840_status & 0x07;
  725.         return m6840_status;
  726.     }
  727.  
  728.     /* offsets 2, 4, and 6 are Read Timer Counter commands */
  729.     else if ((offset & 1) == 0)
  730.     {
  731.         int counter = (offset - 2) / 2;
  732.         int result = compute_counter(counter);
  733.  
  734.         /* clear the interrupt if the status has been read */
  735.         if (m6840_status_read_since_int & (1 << counter))
  736.             m6840_status &= ~(1 << counter);
  737.         update_interrupts();
  738.  
  739.         m6840_lsb_buffer = result & 0xff;
  740.  
  741.         LOG(("%06X:Counter %d read = %04X\n", cpu_getpreviouspc(), counter, result));
  742.         return result >> 8;
  743.     }
  744.  
  745.     /* offsets 3, 5, and 7 are LSB buffer registers */
  746.     else
  747.         return m6840_lsb_buffer;
  748. }
  749.  
  750.  
  751. WRITE_HANDLER( mcr68_6840_upper_w )
  752. {
  753.     if (!(data & 0xff000000))
  754.         mcr68_6840_w_common(offset / 2, (data >> 8) & 0xff);
  755. }
  756.  
  757.  
  758. WRITE_HANDLER( mcr68_6840_lower_w )
  759. {
  760.     if (!(data & 0x00ff0000))
  761.         mcr68_6840_w_common(offset / 2, data & 0xff);
  762. }
  763.  
  764.  
  765. READ_HANDLER( mcr68_6840_upper_r )
  766. {
  767.     return (mcr68_6840_r_common(offset / 2) << 8) | 0x00ff;
  768. }
  769.  
  770.  
  771. READ_HANDLER( mcr68_6840_lower_r )
  772. {
  773.     return mcr68_6840_r_common(offset / 2) | 0xff00;
  774. }
  775.